<template>
<div class="machine-file-list full-height">
  <div class="file-list-head row no-wrap bg-primary text-white">
    <span class="file-list-path ellipsis" ref="headPath">
      {{path}}
      <q-tooltip v-show="path.length > 0" :delay="1000" anchor="bottom left" self="bottom left">{{path}}</q-tooltip>
    </span>
    <q-space></q-space>
    <q-icon v-if="allowBack" name="iconfont icontubiao-58" class="icon" @click="back()">
      <q-tooltip>返回上一级</q-tooltip>
    </q-icon>
    <q-icon :name="lookStatus==='list'?'iconfont icontubiao-104':'iconfont icontubiao-57'" class="icon" @click="changeLookStatus">
      <q-tooltip>切换显示模式</q-tooltip>
    </q-icon>
    <q-icon name="iconfont icontubiao-34" class="icon" @click="refresh">
      <q-tooltip>刷新</q-tooltip>
    </q-icon>
  </div>
  <div class="list-content drop-target relative-position" @dragenter="onDragEnter"
       @dragleave="onDragLeave" @dragover="onDragOver" @drop="onDrop"
       @mousedown="boxStart($event,'fileList')" ref="fileList"
       >
    <div class="full-height send-many" v-if="sendStatus==='many'">
      <div class="send-many-notice text-center">
        <p>将文件拖动到此处批量发送</p>
        <p>{{machineTickCncNodes.length}}台设备内部存储</p>
        <p>{{machineTickBoxNodes.length}}台设备外部存储</p>
      </div>
    </div>
    <div class="full-height" v-else-if="sendStatus!=='many'&&lookStatus==='bigIcon'" @click.self="clearActFile()">
      <q-scroll-area :thumb-style="thumbStyle" class="full-height">
        <div class="full-height list">
          <dl v-for="(file, fileIndex) in files" :key="fileIndex"
              :class="{'active': isActDay(file)}"
              @mousedown="selFile($event,file)" @dblclick="dblclickE(file)"
              @contextmenu.stop="showMenu($event, 'menu'+fileIndex, file)"
              class="file-item"
              :draggable="file.isFile" @dragstart="onDragStart($event, file)">
            <dt v-if="file.isFile"><q-icon :name="'img:statics/svg/'+file.type+'.svg'" style="font-size: 36px;"></q-icon></dt>
            <dt v-else><q-icon name="img:statics/svg/file-icon.svg" style="font-size: 36px;"></q-icon></dt>
            <dd>
              <div v-if="!file.edit" class="ellipsis-2-lines txt-ellipsis">{{file.label}}</div>
              <q-input class="name renameInput" :class="{'hidden':!file.edit}" v-model="rename" outlined autogrow
                       :ref="'renameInput1'+file.id" @keydown.native.enter="reNameBlur(file, $event)" @blur="setNewNameFun(file, $event)"/>
            </dd>
            <q-tooltip :delay="1000" anchor="bottom middle" self="center middle">{{file.label}}</q-tooltip>
            <q-menu :ref="'menu'+fileIndex" context-menu no-parent-event>
              <q-list dense style="min-width: 150px">
                <q-item clickable v-close-popup v-if="!file.isFile" @click="dblclickE(file)">
                  <q-item-section>打开下一级</q-item-section>
                </q-item>
                <q-item
                  v-stat="{id:CommonConfig.statData[statPos].device.rename.id,name:CommonConfig.statData[statPos].device.rename.name}"
                  clickable v-close-popup @click="renameFun(file)">
                  <q-item-section>重命名</q-item-section>
                </q-item>
                <q-item clickable v-close-popup v-if="file.isFile" @click="addCompareList(file)">
                  <q-item-section>加入对比列表</q-item-section>
                </q-item>
                <q-item
                  v-stat="{id:CommonConfig.statData[statPos].device.edit.id,name:CommonConfig.statData[statPos].device.edit.name}"
                  clickable v-close-popup v-if="file.isFile" @click="editFile(file)">
                  <q-item-section>编辑</q-item-section>
                </q-item>
                <q-item
                  v-stat="{id:CommonConfig.statData[statPos].device.del.id,name:CommonConfig.statData[statPos].device.del.name}"
                  clickable v-close-popup @click="deleteFun(file)">
                  <q-item-section>删除</q-item-section>
                </q-item>
              </q-list>
            </q-menu>
          </dl>
        </div>
      </q-scroll-area>
      <q-menu touch-position context-menu>
        <q-list dense style="min-width: 150px">
          <q-item clickable v-close-popup @click="newFolder">
            <q-item-section>新建文件夹</q-item-section>
          </q-item>
          <q-item
            v-stat="{id:CommonConfig.statData[statPos].device.add.id,name:CommonConfig.statData[statPos].device.add.name}"
            clickable v-close-popup @click="newFile">
            <q-item-section>新建文件</q-item-section>
          </q-item>
        </q-list>
      </q-menu>
    </div>
    <div class="full-height" v-else @click.self="clearActFile">
      <q-table class="table-file" :pagination="pagination" no-data-label="暂无数据"
               :data="files" :columns="columns" row-key="id" hide-bottom
               binary-state-sort @request="onRequest">
        <template v-slot:body="props">
          <q-tr :props="props" @click.native.stop="selFile($event,props.row)" @dblclick.native.stop="dblclickE(props.row)"
                :class="{'active': isActDay(props.row)}"
                @contextmenu.native.stop="showMenu($event, 'menu'+props.row.index, props.row)"
                :draggable="props.row.isFile" @dragstart="onDragStart($event, props.row)" class="file-item"
          >
            <q-td key="label" :props="props">
              <q-icon class="vertical-middle" v-if="props.row.isFile" :name="'img:statics/svg/'+props.row.type+'.svg'" style="font-size:18px;" />
              <q-icon class="vertical-middle" v-else name="img:statics/svg/file-icon.svg" style="font-size:18px;" />
              <span v-if="!props.row.edit" class="margin-l-10 name ellipsis">
                {{ props.row.label }}
                <q-tooltip :delay="1000">{{props.row.label}}</q-tooltip>
              </span>
              <q-input class="margin-l-10 name renameInput" :class="{'hidden':!props.row.edit}" v-model="rename" dense outlined
                       :ref="'renameInput2'+props.row.id" @keydown.native.enter="reNameBlur(props.row, $event)"
                       @blur="setNewNameFun(props.row, $event)" />
            </q-td>
            <q-td key="size" :props="props">{{ props.row.isFile?sizeFormat(props.row.size):'—' }}</q-td>
            <q-td key="timestamp" :props="props">{{ dateFormat(props.row.timestamp) }}</q-td>
            <q-menu :ref="'menu'+props.row.index" touch-position context-menu no-parent-event>
              <q-list dense style="min-width: 150px">
                <q-item clickable v-close-popup v-if="!props.row.isFile" @click="dblclickE(props.row)">
                  <q-item-section>打开下一级</q-item-section>
                </q-item>
                <q-item
                  v-stat="{id:CommonConfig.statData[statPos].device.rename.id,name:CommonConfig.statData[statPos].device.rename.name}"
                  clickable v-close-popup @click="renameFun(props.row)">
                  <q-item-section>重命名</q-item-section>
                </q-item>
                <q-item clickable v-close-popup v-if="props.row.isFile" @click="addCompareList(props.row)">
                  <q-item-section>加入对比列表</q-item-section>
                </q-item>
                <q-item
                  v-stat="{id:CommonConfig.statData[statPos].device.edit.id,name:CommonConfig.statData[statPos].device.edit.name}"
                  clickable v-close-popup v-if="props.row.isFile" @click="editFile(props.row)">
                  <q-item-section>编辑</q-item-section>
                </q-item>
                <q-item
                  v-stat="{id:CommonConfig.statData[statPos].device.del.id,name:CommonConfig.statData[statPos].device.del.name}"
                  clickable v-close-popup @click="deleteFun(props.row)">
                  <q-item-section>删除</q-item-section>
                </q-item>
              </q-list>
            </q-menu>
          </q-tr>
        </template>
      </q-table>
      <q-menu touch-position context-menu>
        <q-list dense style="min-width: 150px">
          <q-item clickable v-close-popup @click="newFolder">
            <q-item-section>新建文件夹</q-item-section>
          </q-item>
          <q-item
            v-stat="{id:CommonConfig.statData[statPos].device.add.id,name:CommonConfig.statData[statPos].device.add.name}"
            clickable v-close-popup @click="newFile">
            <q-item-section>新建文件</q-item-section>
          </q-item>
        </q-list>
      </q-menu>
    </div>
    <div class="box-select" ref="boxSelect"></div>
  </div>
<!--  文件内容编辑插件-->
  <code-edit ref="codeEdit" :value="editFileContent" :name="editFileName" :decode="edtiFileDecode"
             :path="editFilePath" :state="codeState" @refreshList="refresh" @save="saveFile"></code-edit>
  <!--  文件下载确认弹框-->
  <send-sure-dialog ref="sendSure" :sender="sender" :acceptor="acceptor"
                    @removeItem="removeItem" @submit="sendMachine"></send-sure-dialog>
</div>
</template>

<script>
import { getFolderChildList, deleteDirectory, rmFile,
  getFilesForDict, getFileStats } from 'src/assets/js/file-dir'
import { getOSSAccount, readFile } from 'src/boot/program/cloud'
import { newCncDir, newBoxDir, delBoxfile, delCncfile, renameCncfile, renameBoxfile,
  fileLocalMachine, fileMachineCloud, machineNewFile, fileMachineLocal, cncToBox } from 'src/boot/program/machine'
import { date } from 'quasar'
import CodeEdit from 'src/components/code-edit/index'
import SendSureDialog from './send-sure-dialog'
import { loadOss, saveOss } from 'src/assets/js/storage'
import { matchFileType, getOffsetLeft, getOffsetTop, mqttErrMessage } from 'src/assets/js/util'
import { mapGetters } from 'vuex'
import boxErr from 'src/assets/json/boxErr'
import boxCodeErr from 'src/assets/json/boxCodeErr'
/* eslint-disable */
const fs = require('fs')
let OSS = require('ali-oss')

export default {
  name: 'file-list',
  props: {
    // 发送状态 单台发送single 多台发送many
    sendStatus: {
      type: String
    },
    // 选中的树节点
    nodeAct: {
      type: Object
    },
    // 选中的文件
    seleFiles: {
      type: Array
    },
    state: { // 'local' or 'cloud'
      type: String,
      default: 'machine'
    },
    // 拖动item
    dragItem: {
      type: Array
    },
    // 邻居节点
    siblingNode: {
      type: Object
    },
    client: {},
    // mqtt唯一标识
    requestId: {
      type: String
    },
    // 发送多台时的设备数据
    machineTickNodes: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 埋点所在位置
    statPos: {
      type: String
    }
  },
  computed: {
    // 是否显示返回上一级按钮
    allowBack () {
      return this.nodeAct.path.length > 0 && this.nodeAct.path !== '/'
    },
    // 当前路径
    path () {
      return this.nodeAct.path
    },
    // 内部存储数据
    machineTickBoxNodes () {
      return this.machineTickNodes.filter(item => item.position === 'box')
    },
    // 外部存储数据
    machineTickCncNodes () {
      return this.machineTickNodes.filter(item => item.position === 'cnc')
    },
    ...mapGetters(['userInfo'])
  },
  data () {
    return {
      thumbStyle: this.CommonConfig.thumbStyle, // 滚动条样式
      actFile: [], // 选中的文件
      clickTime: null, // 区分单击与双击
      lookStatus: 'bigIcon', // 查看状态, list or bigIcon
      columns: [ // 列表的头部
        { name: 'label', align: 'left', label: '文件名', field: 'label', style: 'width: 60%' },
        { name: 'size', align: 'left', label: '大小', field: 'size' },
        { name: 'timestamp', label: '修改时间', field: 'timestamp', sortable: true }
      ],
      pagination: {
        sortBy: '',
        descending: false,
        page: 1,
        rowsPerPage: 0,
        rowsNumber: 10
      },
      rename: '', // 重命名
      files: [],
      emptyMenu: false, // 控制空白处右键显示的菜单
      editFileContent: '', // 要编辑文件的内容
      editFileName: '', // 要编辑文件的名字
      edtiFileDecode: '', // 要编辑文件的编码
      editFilePath: '', // 要编辑文件的路径
      codeState: 'edit', // 添加编辑状态，state or add
      sender: { // 发送方
        source: 'local',
        data: []
      },
      acceptor: { // 接收方
        source: 'machine',
        data: []
      },
      programArr: [], //传输到机器文件列表的数据暂存
      transfering: false, // 是否在传输中
      // client: null, // 文件传输用
      isClickEvent: false, // mousedown和click冲突
      clientCloud: null,
      // 框选数据
      parentW: 0, // 外框的宽
      parentH: 0, // 外框的高
      parentX: 0, // 外框的横坐标
      parentY: 0 // 外框的中坐标
    }
  },
  watch: {
    seleFiles (newVal) {
      this.actFile = []
      this.files = newVal.map((item, index) => {
        return {
          index,
          ...item,
          edit: false
        }
      })
    }
  },
  created () {
    this.files = this.seleFiles.map(item => {
      return {
        ...item,
        edit: false
      }
    })
  },
  methods: {
    // 切换显示模式
    changeLookStatus () {
      this.lookStatus = this.lookStatus === 'list' ? 'bigIcon' : 'list'
    },
    setFile (newVal) {
      this.actFile = []
      this.files = JSON.parse(JSON.stringify(newVal))
    },
    // 选中文件
    selFile (e, item) {
      clearTimeout(this.clickTime)
      // const self = this
      this.clickTime = setTimeout(() => {
        if (e.ctrlKey) {
          // 按住ctrl键
          const index = this.actFile.findIndex(file => {
            return file.id === item.id
          })
          if (index > -1) {
            this.actFile = this.actFile.filter(file => {
              return file.id !== item.id
            })
          } else {
            this.actFile.push(item)
          }
        } else if (e.shiftKey) {
          // 按住shift键
          if (this.actFile.length === 0) {
            this.actFile.push(item)
          } else {
            const maxIndex = this.getMaxIndex()
            const index = this.seleFiles.findIndex(file => {
              return file.id === item.id
            })
            let actFile = this.seleFiles.filter((file, fileIndex) => {
              const condition = (index > maxIndex && fileIndex <= index && fileIndex >= maxIndex) ||
                (index < maxIndex && fileIndex >= index && fileIndex <= maxIndex) ||
                (index === maxIndex && fileIndex === index)
              return condition
            })
            this.actFile = actFile
          }
        } else {
          // 单选
          const index = this.actFile.findIndex(file => {
            return file.id === item.id
          })
          this.actFile = index > -1 ? [] : [item]
        }
        this.$emit('refreshSelFile', 'machine', this.actFile)
      }, 250)
    },
    // 获取存储在选中的file里面的最大最小角标
    getMaxIndex () {
      const indexArr = []
      this.actFile.forEach(item => {
        const index = this.seleFiles.findIndex(file => {
          return file.id === item.id
        })
        if (index > -1) {
          indexArr.push(index)
        }
      })
      indexArr.sort((a, b) => a > b ? 1 : -1)
      return indexArr[indexArr.length - 1]
    },
    // 是否是选中状态
    isActDay (file) {
      const index = this.actFile.findIndex(item => {
        return item.id && item.id + '' === file.id + ''
      })
      return index > -1
    },
    // 清空选中文件列表
    clearActFile (event) {
      if (event.target.className === '')
      this.actFile = []
      this.$emit('refreshSelFile', 'local', this.actFile)
    },
    clearActFile1 () {
      this.actFile = []
    },
    // 双击进入子级文件夹
    dblclickE (file) {
      clearTimeout(this.clickTime)
      if (!file.isFile) {
        this.actFile = []
        let { dirs, nodes } = getFolderChildList(file)
        this.$emit('infoDir', file, nodes, dirs)
        // this.isShowPathDetail()
      }
    },
    // 返回上一级
    back () {
      this.$emit('back')
    },
    // 时间格式化
    dateFormat (timestamp) {
      return date.formatDate(timestamp, 'YYYY-MM-DD HH:mm:ss')
    },
    // 文件大小格式化
    sizeFormat (size) {
      if (size === 0) {
        return '0B'
      } else if (size / 1024 < 1) {
        return size + 'B'
      } else if (size / 1024 / 1024 < 1) {
        return (size / 1024).toFixed(2) + 'K'
      } else if (size / 1024 / 1024 / 1024 < 1) {
        return (size / 1024 / 1024).toFixed(2) + 'M'
      } else {
        return (size / 1024 / 1024 / 1024).toFixed(2) + 'G'
      }
    },
    // 刷新
    refresh () {
      // let { dirs, nodes } = getFolderChildList(this.nodeAct)
      // this.$emit('infoDir', this.nodeAct, nodes, dirs)
      if (this.path.length === 0) {
        this.errMessage('当前没有选中的目录')
        return
      }
      this.$emit('refreshInfo')
    },
    // 重命名显示输入框
    renameFun (item) {
      this.rename = item.label
      this.resetEdit(item)
      // 设置选中区域
      this.$nextTick(() => {
        const input = this.$refs['renameInput' + (this.lookStatus === 'list' ? 2 : 1) + item.id]
        const dominput = input.constructor === Array ? input[0].$refs.input : input.$refs.input
        const endPos = this.getTxtPos(item.label)
        if (dominput.setSelectionRange) {
          dominput.focus()
          dominput.setSelectionRange(0, endPos)
        } else if (dominput.createTextRange) {
          var range = dominput.createTextRange
          range.collapse(true)
          range.moveStart('character', 0)
          range.moveEnd('character', endPos)
          range.select()
        }
      })
    },
    // 获取选中区域的终止位置
    getTxtPos (label) {
      let txtArr = label.split('.')
      if (txtArr.length === 1) {
        return label.length
      }
      const lastLength = txtArr[txtArr.length - 1].length
      return label.length - lastLength - 1
    },
    // 设置编辑状态
    resetEdit (actItem) {
      this.files = this.files.map(item => {
        return {
          ...item,
          edit: actItem.id === item.id
        }
      })
    },
    // 退出重命名状态
    blur () {
      this.files = this.files.map(item => {
        return {
          ...item,
          edit: false
        }
      })
    },
    // 重命名文件夹
    setNewNameFun (item, event) {
      event.preventDefault()
      this.$emit('onSubscribe', item.boxId)
      // 如果名字没有变化，就这样了
      if (item.label === this.rename) {
        // event.target.blur()
        item.edit = false
        return
      }

      // let pathArr = item.path.split('/')
      // let newPath = pathArr.splice(0, pathArr.length - 1).join('/') + '/' + this.rename
      let newPath = this.nodeAct.path + '/' + this.rename + (item.isFile ? '' : '/')
      let params = {
        boxId: item.boxId,
        requestId: this.requestId,
        newPath: newPath,
        oldPath: item.path,
      }
      if (this.nodeAct.position === 'cnc') {
        renameCncfile(params)
      } else if (this.nodeAct.position === 'box') {
        renameBoxfile(params)
      }

      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        try {
          if (data.request_id === this.requestId && data.code === 200) {
            if (data.error !== 0) {
              this.rename = item.label
              // 报错
              mqttErrMessage(data.error)
            } else {
              // 刷新数据
              this.refresh()
            }
          }
        } catch (e) {
          console.error(e)
          this.rename = item.label
          this.errMessage('重命名失败')
        }
      }
    },
    reNameBlur (item, event) {
      event.target.blur()
    },
    // 删除文件和文件夹，机器端取消批量删除
    deleteFun (item) {
      this.$q.dialog({
        title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 确定要删除${item.label}吗？</p>`,
        message: '删除后无法恢复',
        html: true,
        ok: {
          unelevated: true,
          label: '确认'
        },
        cancel: {
          unelevated: true,
          outline: true,
          label: '取消'
        }
      }).onOk(() => {
        this.$emit('onSubscribe', item.boxId)
        // if (this.actFile.length > 0) {
        //   this.batchDeleteFun()
        //   return false
        // }
        let params = {
          boxId: item.boxId,
          requestId: this.requestId,
          path: item.path
        }
        if (item.position === 'cnc') {
          delCncfile(params)
        } else if (item.position === 'box') {
          delBoxfile(params)
        }

        this.client.onMessageArrived = (message) => {
          let data = JSON.parse(message.payloadString)
          try {
            if (data.request_id === this.requestId && data.code === 200) {
              if (data.error === 0) {
                // 刷新数据
                this.refresh()
              } else {
                mqttErrMessage(data.error)
              }
            }
          } catch (e) {
            console.error(e)
            this.errMessage('删除失败')
          }

        }
      })
    },
    // 批量删除
    batchDeleteFun () {
      const finalArr = { success: [], fail: [] }
      this.actFile.forEach(item => {
        if (item.isFile) {
          let path = item.path
          rmFile(path, (err) => {
            if (err) {
              console.warn(err)
              finalArr.fail.push(item)
            }
            finalArr.success.push(item)
            if (finalArr.fail.length + finalArr.success.length === this.actFile.length) {
              let message = ''
              finalArr.fail.forEach(item => {
                message += item.label
              })
              message += '删除失败'
              this.errMessage(message)
              this.refresh()
            }
          })
        } else {
          // let path = item.path.slice(0, item.path.length - 1)
          let path = item.path
          deleteDirectory(path).then(() => {
            finalArr.success.push(item)
          }).catch(() => {
            this.errMessage('删除文件夹失败')
            finalArr.fail.push(item)
          }).finally(() => {
            if (finalArr.fail.length + finalArr.success.length === this.actFile.length) {
              let message = ''
              if (finalArr.fail.length > 0) {
                finalArr.fail.forEach(item => {
                  message += item.label
                })
                message += '删除失败'
                this.errMessage(message)
              }
              this.refresh()
              this.actFile = []
            }
          })
        }
      })
    },
    // 新建文件夹
    newFolder () {
      this.$q.dialog({
        class: 'prompt-dialog',
        title: '文件夹名称',
        prompt: {
          model: '',
          type: 'text'
        },
        ok: {
          unelevated: true,
          label: '确认'
        },
        cancel: {
          unelevated: true,
          outline: true,
          label: '取消'
        },
        persistent: true
      }).onOk(data => {
        if (data.length === 0) {
          this.$q.dialog({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
            message: '名称不能为空',
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          return false
        }
        if (data.length > 20) {
          this.$q.dialog({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
            message: '名称长度必须小于20',
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          return false
        }
        let findIndex = this.files.findIndex(item => !item.isFile && item.label === data)
        if (findIndex > -1) {
          this.$q.dialog({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
            message: `文件夹${data}已存在`,
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          return false
        }
        this.$emit('onSubscribe', this.nodeAct.boxId)
        let params = {
          boxId: this.nodeAct.boxId,
          requestId: this.requestId,
          path: this.nodeAct.path + data
        }
        if (this.nodeAct.position === 'cnc') {
          newCncDir(params)
        } else if (this.nodeAct.position === 'box') {
          newBoxDir(params)
        }

        this.client.onMessageArrived = (message) => {
          let data = JSON.parse(message.payloadString)
          try {
            if (data.request_id === this.requestId && data.code === 200) {
              if (data.error === 0) {
                // 刷新数据
                this.refresh()
              } else {
                mqttErrMessage(data.error)
              }
            }
          } catch (e) {
            console.error(e)
            this.errMessage('新建文件夹失败')
          }

        }
      })
    },
    // 新建文件
    newFile () {
      this.$q.dialog({
        class: 'prompt-dialog',
        title: '文件名称',
        prompt: {
          model: '',
          type: 'text'
        },
        ok: {
          unelevated: true,
          label: '确认'
        },
        cancel: {
          unelevated: true,
          outline: true,
          label: '取消'
        },
        persistent: true
      }).onOk(data => {
        if (data.length === 0) {
          this.$q.dialog({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
            message: '名称不能为空',
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          return false
        }
        if (data.length > 20) {
          this.$q.dialog({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
            message: '名称长度必须小于20',
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          return false
        }
        let findIndex = this.files.findIndex(item => item.isFile && item.label === data)
        if (findIndex > -1) {
          this.$q.dialog({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
            message: `文件${data}已存在`,
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          return false
        }
        this.editFilePath = this.nodeAct.path + data
        this.editFileName = data
        this.editFileContent = ''
        this.edtiFileDecode = ''
        this.codeState = 'add'
        this.$refs.codeEdit.showDialog()
      })
    },
    // 失败提示
    errMessage (message) {
      this.$q.dialog({
        title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 提示</p>`,
        message: message,
        html: true,
        ok: {
          unelevated: true,
          label: '确认'
        }
      })
    },
    // 右键显示选中的文件或文件夹的菜单
    showMenu (event, ref, item) {
      const ele = this.$refs[ref]
      if (ele) {
        ele.constructor === Array ? this.$refs[ref][0].show(event) : this.$refs[ref].show(event)
      }
      const index = this.actFile.findIndex(file => {
        return file.id === item.id
      })
      this.actFile = index > -1 ? [] : [item]
      this.$emit('refreshSelFile', 'machine', this.actFile)
    },
    // 编辑读取文件
    editFile (item) {
      if (!this.testFileSize(item)) return
      this.editFileName = item.label
      if (matchFileType(item.label)) {
        this.errMessage('无法编辑图片文件、word文件、视频文件、音频文件、快捷方式、压缩文件！')
        return
      }
      if (item.position === 'cnc') {
        this.putMachineInside(item)
      } else {
        this.putMachine(item, item.path)
      }
    },
    // 验证文件大小是否超过5M
    testFileSize (file) {
      if (file.size > 5 * 1024 * 1024) {
        this.errMessage('文件超过5M暂不支持查看！')
        return false
      }
      return true
    },
    // 开始发送内部存储文件到外部存储文件
    putMachineInside (send) {
      let requestId1 = 'webClient' + this.userInfo.user_id.toString() + Date.parse(new Date()).toString()
      let self = this
      let params1 = {
        boxId: send.boxId,
        cncPath: send.path,
        requestId: requestId1
      }
      this.$emit('onSubscribe', send.boxId)
      cncToBox(params1)
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        try {
          // 这里是放置对其他消息进行处理
          if (requestId1 === data.request_id && data.hasOwnProperty('error')) {
            if (data.error === 0) {
              this.putMachine(send, data.file_path)
            } else {
              this.errMessage(`文件内容读取失败！${boxErr[data.code]}`)
            }
          }
        } catch (e) {
          console.error('文件内容读取失败！')
        }

      }
    },
    // 从机床传输到云端的方法
    putMachine (send, path) {
      let self = this
      let params = {
        boxId: send.boxId,
        fileName: send.label,
        filePath: path,
        fromBox: send.position === 'box' ? 1 : 0
      }
      let requestId, cloudDownloadUrl
      fileMachineLocal(params).then(res => {
        requestId = res.data.requestId
        cloudDownloadUrl = res.data.url
        this.$emit('onSubscribe2', res.data.requestId)
      })
      // 上传开始时间
      let startTime = new Date()
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        try {
          // 这里是放置对其他消息进行处理
          if (requestId === data.request_id) {
            if (data.hasOwnProperty('source') && data.source === 'send_result') {
              if (data.code === 200) {
                // 盒子上传到云端已完成，接下来是从云端获取内容
                this.getFileContent(send, data.url)
              } else {
                this.errMessage(`文件内容读取失败！${boxCodeErr[data.code]}`)
              }
            }
          }
        } catch (e) {
          console.error(e)
          this.errMessage(`文件内容读取失败！`)
        }
      }
    },
    // 通过云端接口读取云端文件内容
    getFileContent (send, sendPath) {
      readFile(sendPath).then(res => {
        this.editFileContent = res.data
        this.editFilePath = send.path
        this.codeState = 'edit'
        this.$refs.codeEdit.showDialog()
      })
    },
    // 保存文件
    saveFile (path, code) {
      if (this.nodeAct.position === 'cnc' && this.codeState==='edit') {
        this.delEditFile(path, code)
      } else {
        this.saveFileFuc(path, code)
      }
    },
    // 编辑操作，保存时，先删掉内部存储对应的文件
    delEditFile (path, code) {
      this.$emit('onSubscribe', this.nodeAct.boxId)
      let params = {
        boxId: this.nodeAct.boxId,
        requestId: this.requestId,
        path: path
      }
      delCncfile(params)

      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        try {
          if (data.request_id === this.requestId && data.code === 200) {
            if (data.error === 0) {
              // 刷新数据
              this.saveFileFuc(path, code)
            } else {
              mqttErrMessage(data.error)
            }
          }
        } catch (e) {
          console.error(e)
          this.errMessage('保存失败')
        }
      }
    },
    // 通知后台报错文件
    saveFileFuc (path, code) {
      let params = {
        boxId: this.nodeAct.boxId,
        content: code,
        fileName: this.editFileName,
        storagePath: this.nodeAct.path,
        toCnc: this.nodeAct.position === 'cnc' ? 1 : 0
      }
      let requestId
      machineNewFile(params).then(res => {
        requestId = res.data.requestId
        this.$emit('onSubscribe2', res.data.requestId)
        if (this.nodeAct.position === 'cnc') {
          this.$emit('onSubscribe', this.nodeAct.boxId)
        }
        this.editFileContent = ''
        this.$refs.codeEdit.hideDialog()
      })
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        console.log(message)
        try {
          if (requestId === data.request_id) {
            // 判断外部存储新建文件完成
            if (data.source === 'send_result') {
              if (data.code === 200) {
                let successMessage = `<p class="dialog-notice-title"><i class="iconfont icontubiao-53"></i> ${this.codeState==='edit'?'修改':'添加'}文件${this.editFileName}成功</p>`
                this.$q.dialog({
                  title: successMessage,
                  html: true,
                  ok: {
                    unelevated: true,
                    label: '确认'
                  }
                })
                this.refresh()
              } else if (data.code !== 200) {
                this.errMessage(`${this.codeState==='edit'?'修改':'添加'}文件${this.editFileName}失败！${boxCodeErr[data.code]}`)
              }
            } else if (data.hasOwnProperty('error')) {
              // 判断内部存储新建文件完成
              if (data.error === 0) {
                let successMessage = `<p class="dialog-notice-title"><i class="iconfont icontubiao-53"></i> ${this.codeState==='edit'?'修改':'添加'}文件${this.editFileName}成功</p>`
                this.$q.dialog({
                  title: successMessage,
                  html: true,
                  ok: {
                    unelevated: true,
                    label: '确认'
                  }
                })
                this.refresh()
              } else {
                this.errMessage(`${this.codeState==='edit'?'修改':'添加'}文件${this.editFileName}失败！${boxErr[data.error]}`)
              }
            }
          }
        } catch (e) {
          console.log(e)
        }
      }
    },
    // 加入对比列表
    addCompareList (file) {
      this.$emit('showCompareList', file)
      this.actFile = []
    },
    // 拖动功能
    // store the id of the draggable element
    onDragStart (e, dragItem) {
      e.dataTransfer.setData('text', e.target.id)
      e.dataTransfer.dropEffect = 'move'
      const index = this.actFile.findIndex(file => {
        return file.id === dragItem.id
      })
      if (index === -1) {
        // this.actFile.push(dragItem)
        this.actFile = [dragItem]
      }
      this.$emit('setDragItem', this.actFile)
    },
    onDragEnter (e) {
      // don't drop on other draggables
      if (e.target.draggable !== true) {
        e.target.classList.add('drag-enter')
      }
    },
    onDragLeave (e) {
      e.target.classList.remove('drag-enter')
    },
    onDragOver (e) {
      e.preventDefault()
    },
    onDrop (e) {
      e.preventDefault()

      // don't drop on other draggables
      if (e.target.draggable === true || this.dragItem[0].source === 'machine') {
        return
      }
      if (this.sendStatus === 'single' && (this.nodeAct.label === '' || this.nodeAct.path === '')) {
        this.errMessage('请先选择盒子目录')
        return
      } else if (this.sendStatus === 'many' && this.machineTickNodes.length === 0) {
        this.errMessage('请先选择盒子目录')
        return
      }
      console.log(this.dragItem)
      this.sender = {
        source: this.dragItem[0].source,
        data: this.dragItem.filter(item => item.isFile)
      }
      if (this.sendStatus === 'single') {
        this.acceptor = {
          source: 'machine',
          data: [this.nodeAct]
        }
      } else {
        this.acceptor = {
          source: 'machine',
          data: this.machineTickNodes
        }
      }

      this.$refs.sendSure.showDialog()
    },
    // 发送确认框，移除某一项
    removeItem (postion, item) {
      console.log(item)
      console.log(this.acceptor.data)
      if (postion === 'send') {
        this.sender.data = this.sender.data.filter(i => {
          return item.label !== i.label && item.path !== i.path
        })
      }
      if (postion === 'accept') {
        this.acceptor.data = this.acceptor.data.filter(i => {
          return item.label !== i.label || item.id !== i.id
        })
      }
      if (this.sender.data.length === 0 || this.acceptor.data.length === 0) {
        this.$refs.sendSure.hideDialog()
      }
    },
    // 将文件发送给机器
    sendMachine () {
      // 隐藏发送文件确认弹框
      this.$refs.sendSure.hideDialog()
      // 验证文件夹是否存在
      if (this.machineFolderExist()) {
        this.$q.dialog({
          title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
          message: '检测到当前位置存在相同的文件夹，选择继续上传将覆盖文件夹中相同的文件。是否继续上传？',
          html: true,
          ok: {
            unelevated: true,
            label: '确认'
          },
          cancel: {
            unelevated: true,
            outline: true,
            label: '取消'
          },
          persistent: true
        }).onOk(data => {
          if (this.sender.source === 'local') {
            this.upload()
          } else if (this.sender.source === 'cloud') {
            this.getCloudFilesForDic(this.sender.data)
          }
        })
      } else {
        if (this.sender.source === 'local') {
          this.upload()
        } else if (this.sender.source === 'cloud') {
          this.getCloudFilesForDic(this.sender.data)
        }
      }

      // let dirSet = new Set(dirs)
      // let newDirs = Array.from(dirSet)
      // this.newDirs(newDirs)
    },
    // 本地上传进一步验证
    upload () {
      const files = getFilesForDict(this.sender.data)
      if (files.length > 50) { // 超过50个不能上传
        this.errMessage('一次上传不能超过50个文件')
        return false
      }
      if (files.length === 0) {
        this.errMessage('当前没有选择文件')
        return false
      }
      if (!this.testFileSize(this.sender.data)) {
        return false
      }
      // 创建新文件夹
      for (let i = 0; i < this.sender.data.length; i++) {
        // 判断是否是文件夹
        if (!this.sender.data[i].isFile) {
          let findIndex = this.files.findIndex(item => item.label === this.sender.data[i].label)
          if (findIndex > -1) {
            break
          }
          this.$emit('onSubscribe', this.nodeAct.boxId)
          let params = {
            boxId: this.nodeAct.boxId,
            requestId: this.requestId,
            path: this.nodeAct.path + this.sender.data[i].label
          }
          if (this.nodeAct.position === 'cnc') {
            newCncDir(params)
          } else if (this.nodeAct.position === 'box') {
            newBoxDir(params)
          }
        }
      }
      let dirs = []
      let siblingNodePath = this.siblingNode.path.replace(/\\/g, '/')
      this.programArr = []
      // 遍历每个文件，获取blob对象，发送文件
      this.acceptor.data.forEach(accept => {
        files.forEach(send => {
          let path = send[0].replace(/\\/g, '/').replace(siblingNodePath, '').replace(send[1], '')
          if (path.length > 0) {
            dirs.push(path)
          }
          // 发送到单台
          if (this.sendStatus === 'single') {
            // 判断文件是否存在
            let savePath = (accept.path === '' ? this.userInfo.tenant_id + '/' : accept.path) + send[1]
            let exist = this.machineFileExist(savePath, send[1])
            if (exist) {
              if (accept.position === 'cnc') {
                this.$q.dialog({
                  title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
                  message: '文件' + send[1] + '已存在，内部存储不允许覆盖文件',
                  html: true,
                  ok: {
                    unelevated: true,
                    label: '确认'
                  },
                  persistent: true
                })
              } else {
                this.$q.dialog({
                  title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
                  message: `文件${send[1]}已存在，是否覆盖文件?`,
                  html: true,
                  ok: {
                    unelevated: true,
                    label: '确认'
                  },
                  cancel: {
                    unelevated: true,
                    outline: true,
                    label: '取消'
                  },
                  persistent: true
                }).onOk(data => {
                  // 读取文件信息，生成blob
                  this.addProgramArr(send, accept)
                  this.startProgram()
                  // getFileStats(send, accept.path, this.put)
                })
              }
            } else {
              this.addProgramArr(send, accept)
              // 读取文件信息，生成blob
              // getFileStats(send, accept.path, this.put)
            }
          } else if (this.sendStatus === 'many') {
            // 发送到多台，不判断文件是否存在
            this.addProgramArr(send, accept)
            // 读取文件信息，生成blob
            // getFileStats(send, accept.path, this.put)
          }
        })
      })
      this.startProgram()
    },
    // 添加到传输列表
    addProgramArr (send, accept) {
      this.programArr.push({
        send,
        acceptPath: accept.path,
        accept
      })
      let startTime = new Date()
      let path = accept.path.length === 0 ? this.userInfo.tenant_id + '/' : accept.path
      let path1 = send[0].replace(/\\/g, '/').replace(this.siblingNode.path, '')
      let path1s = path1.split('/')
      path1s.pop()
      path1 = path1s.length > 0 ? path1s.join('/') + '/' : ''
      let progress = {
        name: send[1], // 文件名字
        progress: 0, // 传输进度
        state: 'upload', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
        partSize: 16, // 每次上传的大小
        fileSize: 0, // 文件总大小
        acceptPath: path + path1 + send[1], // 接收方地址
        startTime, // 开始时间
        sender: this.sender.source, // 发送方
        acceptor: `${this.acceptor.source}(${accept.position === 'cnc' ? '内部存储' : '外部存储'})`, // 接收方
        endTime: this.dateToString(new Date()),
        useTime: this.getUseTime(startTime, new Date())
      }
      this.$emit('addProgressFiles', progress)
    },
    // 传输一个文件
    startProgram () {
      // 如果此时已在传输中，这次请求就打回
      if (this.transfering) {
        return
      }
      if (this.programArr.length > 0) {
        let program = this.programArr.shift()
        this.transfering = true
        getFileStats(program.send, program.acceptPath, this.put, program.accept)
      }
    },
    // 发送本地文件到机器的方法
    async put (sendFile, send, acceptPath, acceptOpt) {
      let self = this
      let path = acceptPath.length === 0 ? this.userInfo.tenant_id + '/' : acceptPath
      let path1 = send[0].replace(/\\/g, '/').replace(this.siblingNode.path, '')
      let path1s = path1.split('/')
      path1s.pop()
      path1 = path1s.length > 0 ? path1s.join('/') + '/' : ''
      // 获取当前发送信息
      let file = this.sender.data.find(item => {
        return item.label === send[1]
      })
      let requestId
      let startTime = new Date()
      let formdata = new FormData()
      formdata.append("boxId", acceptOpt.boxId)
      formdata.append("file",sendFile)
      formdata.append("storagePath", path + path1)
      formdata.append("toCnc", acceptOpt.position === 'cnc' ? 1 : 0)
      formdata.append("fileName", send[1])
      fileLocalMachine(formdata).then(res => {
        requestId = res.data.requestId
        if (acceptOpt.position === 'cnc') {
          this.$emit('onSubscribe', acceptOpt.boxId)
        }
        this.$emit('onSubscribe1', res.data.requestId)
        this.$emit('onSubscribe2', res.data.requestId)
      })
      // 上一个进度
      let lastProgress = null, filesize = 0
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        console.log(message)
        try {
          if (requestId === data.request_id) {
            // 内部存储逻辑，当data.err===0才是传输成功
            if (acceptOpt.position === 'cnc') {
              // 判断是内部存储发来的消息
              if (message.destinationName === `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${acceptOpt.boxId}`) {
                if (data.hasOwnProperty('error') && data.error === 0) {
                  // 传输成功，开始传下一个
                  this.transfering = false
                  this.startProgram()
                  // 上传成功，刷新列表
                  if (this.nodeAct.path && this.nodeAct.path.length > 0) {
                    this.refresh()
                  }
                  // 报告进度
                  let progress = {
                    name: send[1], // 文件名字
                    progress: 1, // 传输进度
                    state: 'success', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: filesize, // 文件总大小
                    acceptPath: path + path1 + send[1], // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(内部存储)`, // 接收方
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                } else {
                  // 传输失败，开始传下一个
                  this.transfering = false
                  this.startProgram()
                  // 报告进度
                  let progress = {
                    name: send[1], // 文件名字
                    progress: 1, // 传输进度
                    state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: filesize, // 文件总大小
                    acceptPath: path + path1 + send[1], // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(内部存储)`, // 接收方
                    reason: data.hasOwnProperty('error') ? boxErr[data.error] : boxCodeErr[data.code], // 传输失败原因
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                }
              } else if (data.hasOwnProperty('source') && data.source === 'send_result') {
                if (data.code !== 200) {
                  // 传输失败，开始传下一个
                  this.transfering = false
                  this.startProgram()
                  // 报告进度
                  let progress = {
                    name: send[1], // 文件名字
                    progress: 1, // 传输进度
                    state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: filesize, // 文件总大小
                    acceptPath: path + path1 + send[1], // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(内部存储)`, // 接收方
                    reason: boxCodeErr[data.code], // 传输失败原因
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                }
              } else if (lastProgress === null || lastProgress.progress < data.rate) {
                filesize = data.file_size
                // 这里是防止顺序错乱
                let progress = {
                  name: send[1], // 文件名字
                  progress: data.rate, // 传输进度
                  state: 'upload', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                  partSize: 16, // 每次上传的大小
                  fileSize: data.file_size, // 文件总大小
                  acceptPath: path + path1 + send[1], // 接收方地址
                  startTime, // 开始时间
                  sender: self.sender.source, // 发送方
                  acceptor: `machine(内部存储)`, // 接收方
                  endTime: self.dateToString(new Date()),
                  useTime: self.getUseTime(startTime, new Date())
                }
                lastProgress = progress
                self.$emit('addProgressFiles', progress)
              }
            } else {
              // 外部存储逻辑，当data.code===200才是传输成功
              if (data.hasOwnProperty('source') && data.source === 'send_result') {
                if (data.code === 200) {
                  // 传输成功，开始传下一个
                  this.transfering = false
                  this.startProgram()
                  // 上传成功，刷新列表
                  if (this.nodeAct.path && this.nodeAct.path.length > 0) {
                    this.refresh()
                  }
                  // 报告进度
                  let progress = {
                    name: send[1], // 文件名字
                    progress: 1, // 传输进度
                    state: 'success', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: filesize, // 文件总大小
                    acceptPath: path + path1 + send[1], // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(外部存储)`, // 接收方
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                } else {
                  // 传输失败，开始传下一个
                  this.transfering = false
                  this.startProgram()
                  // 报告进度
                  let progress = {
                    name: send[1], // 文件名字
                    progress: 1, // 传输进度
                    state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: filesize, // 文件总大小
                    acceptPath: path + path1 + send[1], // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(外部存储)`, // 接收方
                    reason: boxCodeErr[data.code], // 传输失败原因
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                }
              } else if (lastProgress === null || lastProgress.progress < data.rate) {
                filesize = data.file_size
                // 这里是防止顺序错乱
                let progress = {
                  name: send[1], // 文件名字
                  progress: data.rate, // 传输进度
                  state: 'upload', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                  partSize: 16, // 每次上传的大小
                  fileSize: data.file_size, // 文件总大小
                  acceptPath: path + path1 + send[1], // 接收方地址
                  startTime, // 开始时间
                  sender: self.sender.source, // 发送方
                  acceptor: `machine(外部存储)`, // 接收方
                  endTime: self.dateToString(new Date()),
                  useTime: self.getUseTime(startTime, new Date())
                }
                lastProgress = progress
                self.$emit('addProgressFiles', progress)
              }
            }
          }
        } catch (e) {
          console.error(e)
          // 传输失败，开始传下一个
          this.transfering = false
          this.startProgram()
          // 报告进度
          let progress = {
            name: send[1], // 文件名字
            progress: 1, // 传输进度
            state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
            partSize: 16, // 每次上传的大小
            fileSize: filesize, // 文件总大小
            acceptPath: path + path1 + send[1], // 接收方地址
            startTime, // 开始时间
            sender: self.sender.source, // 发送方
            acceptor: `machine(内部存储)`, // 接收方
            reason: '程序出错', // 传输失败原因
            endTime: self.dateToString(new Date()),
            useTime: self.getUseTime(startTime, new Date())
          }
          self.$emit('addProgressFiles', progress)
        }
      }
    },
    // 判断文件是否存在
    machineFileExist (path, label) {
      let exist = false
      let isFile = true
      if (path[path.length - 1] === '/') {
        isFile = false
      }
      for (let i = 0; i < this.files.length; i++) {
        if (this.files[i].isFile === isFile && this.files[i].label === label) {
          exist = true
          break
        }
      }
      return exist
    },
    // 判断文件夹是否存在
    machineFolderExist (send) {
      for (let i = 0; i < this.sender.data.length; i++) {
        if (!this.sender.data[i].isFile) {
          let findIndex = this.files.findIndex(item => item.label === this.sender.data[i].label)
          if (findIndex > -1) {
            return true
          }
        }
      }
      return false
    },
    // 判断机器传输文件到云端文件是否存在
    machineFileExistMachine (send) {
      if (send.isFile) {
        let findIndex = this.files.findIndex(item => item.isFile &&item.label === send.label)
        if (findIndex > -1) {
          return true
        }
      }
      return false
    },
    // 判断要上传的文件是否有超过50M的
    // 验证要发送的文件是否符合规则
    testFileSize (files) {
      for (let i = 0; i < files.length; i++) {
        if (files[i].size > 50 * 1024 * 1024) {
          this.errMessage(`文件${files[i].label}大小超过50M，请重新选择要上传文件`)
          return false
        }
      }
      return true
    },

    // 云端发送文件到机床的逻辑
    // 获取oss账号
    getOss (func) {
      this.$refs.sendSure.hideDialog()
      let oss = loadOss()
      if (!oss && !oss.expiration) {
        this.refreshOSSAcout(func)
      } else {
        let overTime = new Date(oss.expiration)
        let now = new Date()
        if (now.getTime() < overTime.getTime() - 60) {
          func ? func(oss) : this.sendCloud(oss)
        } else {
          this.refreshOSSAcout(func)
        }
      }
    },
    // 更新oss账号
    refreshOSSAcout (func) {
      getOSSAccount().then(res => {
        let oss = res.data
        saveOss(oss)
        func ? func(oss) : this.sendCloud(oss)
      })
    },
    // 根据云端路径，获取路径下的所有文件
    async getCloudFilesForDic (sendlist) {
      let files = sendlist.filter(item => {
        return item.isFile
      })
      let dirs = sendlist.filter(item => {
        return !item.isFile
      })
      await this.getOss((oss) => {
        this.clientCloud = new OSS({
          region: oss.region,
          accessKeyId: oss.accessKeyId,
          accessKeySecret: oss.accessKeySecret,
          stsToken: oss.securityToken,
          bucket: oss.bucket
        })
        this.getChildByDirs(files, dirs, oss)
      })
    },
    // 获取云端子文件下的所有文件
    async getChildByDirs (files, dirs) {
      let resultChild = []
      for (let i = 0; i < dirs.length; i++) {
        let item = dirs[i]
        let result = await this.clientCloud.list({
          prefix: item.path,
          delimiter: ''
        })
        resultChild = resultChild.concat(result.objects)
      }
      let resultFiles = files
      resultChild.forEach(item => {
        if (item.name[item.name.length - 1] !== '/') {
          let labels = item.name.split('/')
          resultFiles.push({
            ...item,
            label: labels[labels.length - 1],
            path: item.name,
            isFile: true
          })
        }
      })
      if (resultFiles.length > 50) { // 超过50个不能上传
        this.errMessage('一次上传不能超过50个文件')
        return false
      }
      if (resultFiles.length === 0) {
        this.errMessage('当前没有选择文件')
        return false
      }
      // 验证文件大小
      if (!this.testFileSize(resultFiles)) {
        return false
      }
      this.downloadFile(resultFiles)
    },
    // 下载云端文件
    downloadFile (files) {
      let siblingNodePath = this.siblingNode.path.length > 0 ? this.siblingNode.path : this.userInfo.tenant_id + '/'
      this.programArr = []
      // 遍历每个文件，下载文件
      this.acceptor.data.forEach(accept => {
        files.forEach(send => {
          // let path = send.name.replace(siblingNodePath, '')
          let path = send.path.replace(siblingNodePath, '')
          let paths = path.split('/')
          let newAccept = accept
          send.sendPath = send.path.replace(this.siblingNode._name + '/', '').replace(this.userInfo.tenant_id + '/', '')
          // 发送到单台
          if (this.sendStatus === 'single') {
            // 判断文件是否存在
            let savePath = newAccept.path + send.label
            if (this.machineFileExistMachine(send)) {
              if (accept.position === 'cnc') {
                this.$q.dialog({
                  title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
                  message: '文件' + send.label + '已存在，内部存储不允许覆盖文件',
                  html: true,
                  ok: {
                    unelevated: true,
                    label: '确认'
                  },
                  persistent: true
                })
              } else {
                this.$q.dialog({
                  title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i> 提示</p>`,
                  message: '文件' + send.label + '已存在，是否覆盖文件',
                  html: true,
                  ok: {
                    unelevated: true,
                    label: '确认'
                  },
                  cancel: {
                    unelevated: true,
                    outline: true,
                    label: '取消'
                  },
                  persistent: true
                }).onOk(data => {
                  this.addProgramArrCloud(send, newAccept)
                  this.startProgramCloud()
                  // this.putCloud(send, newAccept)
                })
              }
            } else {
              this.addProgramArrCloud(send, newAccept)
              // this.putCloud(send, newAccept)
            }
          } else if (this.sendStatus === 'many'){
            // 发送到多台，不判断文件是否存在
            this.addProgramArrCloud(send, newAccept)
            // this.putCloud(send, newAccept)
          }
        })
      })
      this.startProgramCloud()
    },
    // 添加到传输列表
    addProgramArrCloud (send, accept) {
      this.programArr.push({
        send,
        accept
      })
      let startTime = new Date()
      let progress = {
        name: send.label, // 文件名字
        progress: 0, // 传输进度
        state: 'upload', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
        partSize: 16, // 每次上传的大小
        fileSize: send.size, // 文件总大小
        acceptPath: accept.path + send.label, // 接收方地址
        startTime, // 开始时间
        sender: this.sender.source, // 发送方
        acceptor: `${this.acceptor.source}(${accept.position === 'cnc' ? '内部存储' : '外部存储'})`, // 接收方
        endTime: this.dateToString(new Date()),
        useTime: this.getUseTime(startTime, new Date())
      }
      this.$emit('addProgressFiles', progress)
    },
    // 传输一个文件
    startProgramCloud () {
      // 如果此时已在传输中，这次请求就打回
      if (this.transfering) {
        return
      }
      if (this.programArr.length > 0) {
        let program = this.programArr.shift()
        this.transfering = true
        this.putCloud(program.send, program.accept)
      }
    },
    // 请求后端传输云端文件到机器
    putCloud (send, accept) {
      let self = this
      let params = {
        boxId: accept.boxId,
        cloudPath: send.path,
        storagePath: accept.path,
        toCnc: accept.position === 'box' ? 0 : 1
      }
      let requestId, startTime
      fileMachineCloud(params).then(res => {
        requestId = res.data.requestId
        startTime = new Date()
        this.$emit('onSubscribe1', res.data.requestId)
        this.$emit('onSubscribe2', res.data.requestId)
        if (accept.position === 'cnc') {
          this.$emit('onSubscribe', accept.boxId)
        }
      })
      let progress
      // 上一个进度
      let lastProgress = null
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        console.log(data)
        try {
          if (requestId === data.request_id) {
            // 判断是发给内部存储还是外部存储
            // 发给内部存储
            if (accept.position === 'cnc') {
              // 判断result消息
              if (message.destinationName === `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${accept.boxId}`) {
              // if (data.hasOwnProperty('error')) {
                if (data.error === 0) { // 内部存储发送成功
                  // 传输成功，开始传下一个
                  this.transfering = false
                  this.startProgramCloud()
                  progress = {
                    name: send.label, // 文件名字
                    progress: 1, // 传输进度
                    state: 'success', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: send.size, // 文件总大小
                    acceptPath: accept.path + send.label, // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(内部存储)`, // 接收方
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                  // 上传成功，刷新列表
                  if (this.nodeAct.path && this.nodeAct.path.length > 0) {
                    this.refresh()
                  }
                } else { // 内部存储发送失败
                  // 传输失败，开始传下一个
                  this.transfering = false
                  this.startProgramCloud()
                  console.log(accept.path)
                  progress = {
                    name: send.label, // 文件名字
                    progress: 1, // 传输进度
                    state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: send.size, // 文件总大小
                    acceptPath: accept.path + send.label, // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(内部存储)`, // 接收方
                    reason: data.hasOwnProperty('error') ? boxErr[data.error] : boxCodeErr[data.code], // 传输失败原因
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                }
              } else if (data.source === 'send_result' && data.code !== 200) {
                // 传输失败，开始传下一个
                this.transfering = false
                this.startProgramCloud()
                progress = {
                  name: send.label, // 文件名字
                  progress: 1, // 传输进度
                  state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                  partSize: 16, // 每次上传的大小
                  fileSize: send.size, // 文件总大小
                  acceptPath: accept.path + send.label, // 接收方地址
                  startTime: new Date(), // 开始时间
                  sender: self.sender.source, // 发送方
                  acceptor: `machine(内部存储)`, // 接收方
                  reason: boxCodeErr[data.code], // 传输失败原因
                  endTime: self.dateToString(new Date()),
                  useTime: self.getUseTime(startTime, new Date())
                }
                self.$emit('addProgressFiles', progress)
              } else if (data.source === 'send_info' && (lastProgress === null || lastProgress.progress < data.rate)) {
                // 监听sendInfo发来的进度
                progress = {
                  name: send.label, // 文件名字
                  progress: data.rate / 4 * 3, // 传输进度
                  state: 'upload', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                  partSize: 16, // 每次上传的大小
                  fileSize: send.size, // 文件总大小
                  acceptPath: accept.path + send.label, // 接收方地址
                  startTime, // 开始时间
                  sender: self.sender.source, // 发送方
                  acceptor: `machine(内部存储)`, // 接收方
                  endTime: self.dateToString(new Date()),
                  useTime: self.getUseTime(startTime, new Date())
                }
                self.$emit('addProgressFiles', progress)
              }
            } else { // 发给外部存储
              if (data.source === 'send_result' && data.hasOwnProperty('code')) {
                if (data.code === 200) {
                  // 传输成功，开始传下一个
                  this.transfering = false
                  this.startProgramCloud()
                  progress = {
                    name: send.label, // 文件名字
                    progress: 1, // 传输进度
                    state: 'success', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: send.size, // 文件总大小
                    acceptPath: accept.path + send.label, // 接收方地址
                    startTime, // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `${self.acceptor.source}(外部存储)`, // 接收方
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                  // 发送成功，刷新数据
                  if (this.nodeAct.path && this.nodeAct.path.length > 0) {
                    this.refresh()
                  }
                } else {
                  // 传输失败，开始传下一个
                  this.transfering = false
                  this.startProgramCloud()
                  progress = {
                    name: send.label, // 文件名字
                    progress: 1, // 传输进度
                    state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                    partSize: 16, // 每次上传的大小
                    fileSize: send.size, // 文件总大小
                    acceptPath: accept.path + send.label, // 接收方地址
                    startTime: new Date(), // 开始时间
                    sender: self.sender.source, // 发送方
                    acceptor: `machine(外部存储)`, // 接收方
                    reason: boxCodeErr[data.code], // 传输失败原因
                    endTime: self.dateToString(new Date()),
                    useTime: self.getUseTime(startTime, new Date())
                  }
                  self.$emit('addProgressFiles', progress)
                }
              } else if (data.source === 'send_info' && (lastProgress === null || lastProgress.progress < data.rate)) { // 监听sendInfo发来的进度
                progress = {
                  name: send.label, // 文件名字
                  progress: data.rate, // 传输进度
                  state: 'upload', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
                  partSize: 16, // 每次上传的大小
                  fileSize: send.size, // 文件总大小
                  acceptPath: accept.path + send.label, // 接收方地址
                  startTime, // 开始时间
                  sender: self.sender.source, // 发送方
                  acceptor: `machine(外部存储)`, // 接收方
                  endTime: self.dateToString(new Date()),
                  useTime: self.getUseTime(startTime, new Date())
                }
                self.$emit('addProgressFiles', progress)
              }
            }
          }
        } catch (e) {
          console.error(e)
          this.programErr(send, accept)
        }
      }
    },
    // 代码报错，开始传输下一个
    programErr (send, accept) {
      this.transfering = false
      this.startProgramCloud()
      progress = {
        name: send.label, // 文件名字
        progress: 1, // 传输进度
        state: 'fail', // 上传状态 upload:正在上传，download:正在下载，success：传输成功，fail：传输失败
        partSize: 16, // 每次上传的大小
        fileSize: send.size, // 文件总大小
        acceptPath: accept.path + send.label, // 接收方地址
        startTime, // 开始时间
        sender: self.sender.source, // 发送方
        acceptor: `machine(内部存储)`, // 接收方
        endTime: self.dateToString(new Date()),
        useTime: self.getUseTime(startTime, new Date())
      }
      self.$emit('addProgressFiles', progress)
    },

    // 根据开始时间和结束时间，获取累计用时时间，返回hh:mm:ss
    getUseTime (startTime, endTime) {
      let useTime = (endTime.getTime() - startTime.getTime()) / 1000
      let seconds = parseInt(useTime % 60)
      seconds = seconds < 10 ? '0' + seconds : seconds
      let minutes = parseInt(useTime / 60 % 60)
      minutes = minutes < 10 ? '0' + minutes : minutes
      let hours = parseInt(useTime / 3600 % 60)
      hours = hours < 10 ? '0' + hours : hours
      return `${hours}:${minutes}:${seconds}`
    },
    // 根据Date格式的日期,返回 mm-dd hh:mm
    dateToString (date) {
      let months = date.getMonth() + 1
      months = months < 10 ? '0' + months : months
      let dates = date.getDate()
      dates = dates < 10 ? '0' + dates : dates
      let hours = date.getHours()
      hours = hours < 10 ? '0' + hours : hours
      let minutes = date.getMinutes()
      minutes = minutes < 10 ? '0' + minutes : minutes
      return `${months}-${dates} ${hours}:${minutes}`
    },
    // 框选
    // 鼠标按下事件
    boxStart (event, parentDom) {
      if (event.target.tagName === 'IMG' ||
        event.target.tagName === 'SPAN' ||
        event.target.className.indexOf('txt-ellipsis') > -1) {
        return true
      }
      // 判断鼠标是否是左键按下
      if (event.buttons !== 1 || event.which !== 1) return
      let parentNode = this.$refs[parentDom]
      this.parentX = getOffsetLeft(parentNode)
      this.parentY = getOffsetTop(parentNode)
      this.parentW = parentNode.clientWidth
      this.parentH = parentNode.clientHeight
      let self = this
      clearTimeout(this.mouseTime)
      let startTime = new Date()

      this.mouseOn = true
      this.startX = event.clientX - this.parentX
      this.startY = event.clientY - this.parentY
      let boxSelect = this.$refs.boxSelect
      boxSelect.style.display = 'block'
      boxSelect.style.left = this.startX + 'px'
      boxSelect.style.top = this.startY + 'px'
      boxSelect.style.zIndex = -1
      boxSelect.style.width = 0
      boxSelect.style.height = 0
      document.onmousemove = function (event) {
        self.boxMove(event, parentDom)
      }
      this.$refs[parentDom].onmouseup = function (event) {
        self.$refs[parentDom].onmousemove = null
        clearTimeout(this.mouseTime)
        let endTime = new Date()
        if (endTime.getTime() - startTime.getTime() < 200) {
          self.isClickEvent = true
        }
        self.boxStop(event)
      }
    },
    // 鼠标移动事件
    boxMove (event, parentDom) {
      // 如果框选没有开启。退出
      if (!this.mouseOn) return

      let clientX = event.clientX
      let clientY = event.clientY
      let _x = clientX - this.parentX
      let _y = clientY - this.parentY
      let boxSelect = this.$refs.boxSelect

      boxSelect.style.left = Math.min(_x, this.startX) + 'px'
      boxSelect.style.top = Math.min(_y, this.startY) + 'px'
      boxSelect.style.width = Math.abs(_x - this.startX) + 'px'
      boxSelect.style.height = Math.abs(_y - this.startY) + 'px'
      boxSelect.style.zIndex = 100
      this.findNodes(event)
      // 当框选超出范围时，结束框选操作,取消move和mouseup监听
      if (clientX < this.parentX || clientX > this.parentX + this.parentW || clientY < this.parentY || clientY > this.parentY + this.parentH) {
        document.onmousemove = null
        this.$refs[parentDom].onmouseup = null
        clearTimeout(this.mouseTime)

        this.boxStop(event)
      }
    },
    // 鼠标松开事件
    boxStop (event) {
      this.mouseOn = false
      this.findNodes(event)
      let boxSelect = this.$refs.boxSelect
      boxSelect.style.display = 'none'
      boxSelect.style.width = 0
      boxSelect.style.height = 0
    },
    // 获取框选下的元素
    findNodes (event) {
      let dayNodes = document.getElementsByClassName('machine-file-list')[0].getElementsByClassName('file-item')
      let boxSelect = this.$refs.boxSelect
      let selectedEls = []
      // 获取框的参数
      let l = boxSelect.offsetLeft
      let t = boxSelect.offsetTop
      let w = boxSelect.offsetWidth
      let h = boxSelect.offsetHeight

      for (let i = 0; i < dayNodes.length; i++) {
        let dayNode = dayNodes[i]
        let sl = dayNode.offsetWidth + dayNode.offsetLeft
        let st = dayNode.offsetHeight + dayNode.offsetTop
        if (sl > l && st > t && dayNode.offsetLeft < l + w && dayNode.offsetTop < t + h) {
          selectedEls.push(this.files[i])
        }
      }
      if (event.ctrlKey || event.shiftKey) {
        this.actFile = this.actFile.concat(selectedEls)
      } else {
        this.actFile = selectedEls
      }
    },
    // 排序
    onRequest (props) {
      const { sortBy, descending } = props.pagination
      if (sortBy) {
        const sortFn = (descending
            ? (a, b) => (a[sortBy] > b[sortBy] ? -1 : a[sortBy] < b[sortBy] ? 1 : 0)
            : (a, b) => (a[sortBy] > b[sortBy] ? 1 : a[sortBy] < b[sortBy] ? -1 : 0)
        )
        this.files = this.files.sort(sortFn)
        this.pagination.sortBy = sortBy
        this.pagination.descending = descending
      }
    }
  },
  components: {
    CodeEdit,
    SendSureDialog
  }
}
</script>

<style scoped lang="stylus">
.machine-file-list{
  .file-list-head{
    .file-list-path{
      display inline-block
      width 400px
    }
  }
  .list-content{
    height calc(100% - 40px)
    .send-many{
      position relative
      .send-many-notice{
        position absolute
        top 50%
        left 50%
        margin-top -104px
        margin-left -185px
        width 370px
        height 208px
        border-radius 6px
        border 1px dashed #d3d3d3
        display flex
        flex-direction column
        justify-content center
        p{
          line-height 28px
          font-size 16px
          color #333
          margin-bottom 0
        }
      }
    }
  }
  .list{
    padding 10px
    display flex
    flex-wrap wrap
    dl{
      margin-right 4px
      margin-bottom 4px
      padding 7px 3px
      width 84px
      height 95px
      font-size 14px
      box-sizing border-box
      text-align center
      border 1px solid #fff
      &.active, &:hover{
        background-color rgba(95,119,253, 0.2)
        border 1px solid $primary
      }
    }
    dt{
      text-align center
    }
    dd{
      .renameInput{
        position relative
        z-index 10
        background-color #fff
      }
    }
  }
  .table-file{
    height 100%
    .q-table__middle{
      height 100%
    }
    .active{
      background-color rgba(95,119,253, 0.2)
    }
    .name{
      display inline-block
      width 350px
      vertical-align middle
    }
    .renameInput{
      background-color #fff
    }
  }
}
.file-list-head{
  padding 0 20px
  height 40px
  line-height 40px
  font-size 14px
  .icon{
    margin-top 10px
    margin-left 10px
    font-size 18px
  }
}
.box-select{
  position absolute
  margin 0
  padding 0
  top 0
  left 0
  width 10px
  height 10px
  border 1px dashed $primary
  background-color $primary
  z-index 1000
  opacity 0.6
  display none
}
.txt-ellipsis{
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  word-wrap: break-word;
}
</style>
